/*
 * 
 * Copyright (C) 2010-2012 Wilfried Elmenreich, Anita Sobe 
 * This file is part of VideoNetwork.
 * 
 * VideoNetwork is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>. 
 */
package util;

import java.awt.Color;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import proxy.Content;
import proxy.SearchServerNode;
import proxy.ServerNode;
import proxy.UnitContainer;

public class OutputGenerator {

	private int contentTypes;
	private ArrayList<ServerNode> nodes;

	public OutputGenerator(int contentTypes, ArrayList<ServerNode> nodes) {
		this.contentTypes = contentTypes;
		this.nodes = nodes;
	}

	/**
	 * Generates a pajek description of the current network
	 * 
	 * @param filename
	 *            The filename to store the pajek output to
	 */
	public void serverNodes2pajek(String filename) {
		try {
			// Create a new file output stream
			FileOutputStream fout = new FileOutputStream(filename);
			// Connect print stream to the output stream
			PrintStream p = new PrintStream(fout);

			// Write list of vertices
			p.println("*Vertices " + nodes.size());
			for (int i = 1; i <= nodes.size(); i++) {
				p.println(i + " \"S" + i + "\"");
			}
			// Write list of edges
			p.println("*Edges");
			for (int i = 0; i < nodes.size() - 1; i++) {
				ServerNode nodei = nodes.get(i);
				for (int j = i + 1; j < nodes.size(); j++) {
					if (nodei.containsTarget(nodes.get(j)))
						p.println((i + 1) + " " + (j + 1));
				}
			}
			// Close the output stream
			fout.close();
		} catch (Exception e) {// Catch exception if any
			System.err.println("Error: " + e.getMessage());
		}
	}

	public void serverNodes2CDNSim(String filename) {
		try {
			// Create a new file output stream
			FileOutputStream fout = new FileOutputStream(filename);
			// Connect print stream to the output stream
			PrintStream p = new PrintStream(fout);

			// Write list of edges
			for (int i = 0; i < nodes.size() - 1; i++) {
				ServerNode nodei = nodes.get(i);
				for (int j = i + 1; j < nodes.size(); j++) {
					if (nodei.containsTarget(nodes.get(j)))
						p.println((i + 1) + " " + (j + 1));
				}
			}
			// Close the output stream
			fout.close();
		} catch (Exception e) {// Catch exception if any
			System.err.println("Error: " + e.getMessage());
		}
	}

	/**
	 * Generates a gephi graph output file. The network contains also the units,
	 * colored according to the content type.
	 * 
	 * @param filename
	 *            The filename to store the network description to.
	 */
	public void serverNodes2Gephi(String filename) {

		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
					filename)));
			// header
			bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
			bw.write("<gexf xmlns=\"http://www.gexf.net/1.1draft\" xmlns:viz=\"http://www.gexf.net/1.1draft/viz\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.gexf.net/1.1draft http://www.gexf.net/1.1draft/gexf.xsd\" version=\"1.1\">\n");
			bw.write("<graph defaultedgetype=\"undirected\" idtype=\"string\" type=\"static\">\n");
			// attributes
			bw.write("<attributes class=\"node\">\n");
			// for (int i = 0; i < Simulator.contentTypes; i++) {
			// bw.write("<attribute id=\"" + i + "\" title=\"c" + i +
			// "\" type=\"string\" />\n");
			// }
			bw.write("</attributes>\n");
			// content types (value of content type is normalized)
			// eg: u1: 0.4 1, 0.6 2, 0.0 3

			// nodes
			bw.write("<nodes count=\"" + nodes.size() + "\">\n");
			int edges = 0;
			for (int i = 0; i < nodes.size(); i++) {
				edges += nodes.get(i).getLinks().size();
				int nodeid = nodes.get(i).getId();

				// check which content is there for coloring the node
				// take max
				String unitsForNode = "";
				float[] maxList = new float[contentTypes];
				int count = 0;
				for (Iterator<UnitContainer> it = nodes.get(i)
						.getStoredUnitIterator(); it.hasNext();) {
					count++;
					UnitContainer j = it.next();
					edges++;
					int[] current = j.getVideoUnit().getContent().content;
					for (int k = 0; k < contentTypes; k++) {
						maxList[k] += current[k];
					}
					// add units as vertex around the nodes
					Color c = new Color(current[0] / 100f, current[1] / 100f,
							current[2] / 100f);
					unitsForNode += "<node id=\"S" + nodeid + "_u"
							+ j.getVideoUnit().getId() + "\" label=\"S"
							+ nodeid + "_u" + j.getVideoUnit().getId()
							+ "\">\n";
					unitsForNode += "<viz:color r=\"" + c.getRed() + "\" g=\""
							+ c.getGreen() + "\" b=\"" + c.getBlue() + "\"/>\n";
					unitsForNode += "</node>\n";
				}
				// average + set to RGB
				for (int k = 0; k < contentTypes; k++) {
					maxList[k] = maxList[k] / (count * 100f);
				}
				// server node as vertex
				bw.write("<node id=\"S" + nodeid + "\" label=\"S"
						+ nodes.get(i).getId() + "\">\n");
				Color c = new Color(maxList[0], maxList[1], maxList[2]);
				bw.write("<viz:color r=\"" + c.getRed() + "\" g=\""
						+ c.getGreen() + "\" b=\"" + c.getBlue() + "\"/>\n");
				bw.write("</node>\n");
				bw.write(unitsForNode);
			}
			bw.write("</nodes>\n");
			// edges
			int edgeid = 0;
			bw.write("<edges count=\"" + edges + "\">\n");
			for (int i = 0; i < nodes.size(); i++) {
				for (int j = 0; j < nodes.get(i).getLinks().size(); j++) {
					bw.write("<edge id=\""
							+ edgeid
							+ "\" source=\"S"
							+ nodes.get(i).getId()
							+ "\" target=\"S"
							+ nodes.get(i).getLinks().get(j).getTargetNode()
									.getId() + "\"/>\n");
					edgeid++;
				}
				
				// add units to nodes
				for (Iterator<UnitContainer>it=nodes.get(i).getStoredUnitIterator(); it.hasNext();) {
					
					bw.write("<edge id=\"" + edgeid + "\" source=\"S"
							+ nodes.get(i).getId() + "_u"
							+ it.next().getVideoUnit().getId()
							+ "\" target=\"S" + nodes.get(i).getId() + "\"/>\n");
					edgeid++;
				}
			}
			bw.write("</edges>\n");
			bw.write("</graph>\n");
			bw.write("</gexf>");
			bw.flush();
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void serverNodes2GephiWithHormone(String filename) {
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
					filename)));
			// header
			bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
			bw.write("<gexf xmlns=\"http://www.gexf.net/1.1draft\" xmlns:viz=\"http://www.gexf.net/1.1draft/viz\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.gexf.net/1.1draft http://www.gexf.net/1.1draft/gexf.xsd\" version=\"1.1\">\n");
			bw.write("<graph defaultedgetype=\"undirected\" idtype=\"string\" type=\"static\">\n");
			// attributes
			bw.write("<attributes class=\"node\">\n");
			// for (int i = 0; i < Simulator.contentTypes; i++) {
			// bw.write("<attribute id=\"" + i + "\" title=\"c" + i +
			// "\" type=\"string\" />\n");
			// }
			bw.write("</attributes>\n");
			// content types (value of content type is normalized)
			// eg: u1: 0.4 1, 0.6 2, 0.0 3

			// nodes
			bw.write("<nodes count=\"" + nodes.size() + "\">\n");
			int edges = 0;
			double nodeSize = 10.0;
			ArrayList<Tree<TagNode>> trees = new ArrayList<Tree<TagNode>>();
			for (int i = 0; i < nodes.size(); i++) {
				edges += nodes.get(i).getLinks().size();
				int nodeid = nodes.get(i).getId();
				SearchServerNode node = (SearchServerNode) nodes.get(i);
				// fill levels

				Tree<TagNode> tree = new Tree<TagNode>();
				Node<TagNode> root = new Node<TagNode>(new TagNode(nodeid,
						nodeSize));
				root.data.setId("S" + nodes.get(i).getId());
				tree.setRootElement(root);

				ArrayList<Content> tags = new ArrayList<Content>();
				ArrayList<Node<TagNode>> ids = new ArrayList<Node<TagNode>>();

				String tagsForNode = "";
				for (Content tag : node.getHormoneTable().keySet()) {
					if (tag.content[contentTypes - 1] != 0) {
						Node<TagNode> parent = root;
						for (int j = 0; j < contentTypes - 1; j++) {
							Content gen = node.getGeneralToTag(tag, j);
							TagNode t;
							edges++;
							if (!tags.contains(gen)) {
								tags.add(gen);
								double value;
								if (node.getHormoneTable().containsKey(gen)) {
									value = node.getHormoneTable().get(gen);

								} else {
									value = 0.0;
								}
								t = new TagNode(gen.content[j], value);
								t.setId("S" + nodes.get(i).getId() + "_"
										+ tags.indexOf(gen));

								tagsForNode += "<node id=\"" + t.getId()
										+ "\" label=\"" + gen.content[j]
										+ "\">\n";
								tagsForNode += "<viz:size value=\"" + value
										+ "\"/>\n";
								tagsForNode += "</node>\n";

								Node<TagNode> n = new Node<TagNode>(t);
								n.setParent(parent);
								ids.add(n);
								parent.addChild(n);
								parent = n;
							} else {
								// find node and set parent
								parent = ids.get(tags.indexOf(gen));
							}
						}
						tags.add(tag);
						Node<TagNode> n = new Node<TagNode>(new TagNode(
								tag.content[contentTypes - 1], node
										.getHormoneTable().get(tag)));
						n.data.setId("S" + nodes.get(i).getId() + "_"
								+ tags.indexOf(tag));
						n.setParent(parent);
						ids.add(n);
						parent.addChild(n);
						tagsForNode += "<node id=\"" + n.data.getId()
								+ "\" label=\"" + tag.content[contentTypes - 1]
								+ "\">\n";
						tagsForNode += "<viz:size value=\""
								+ node.getHormoneTable().get(tag) + "\"/>\n";
						tagsForNode += "</node>\n";
						edges++;
						parent = root;
					}

				}

				trees.add(tree);

				// server node as vertex
				bw.write("<node id=\"S" + nodeid + "\" label=\"S"
						+ nodes.get(i).getId() + "\">\n");
				bw.write("<viz:size value=\"" + nodeSize + "\"/>\n");
				bw.write("</node>\n");
				bw.write(tagsForNode);

			}
			bw.write("</nodes>\n");
			// edges
			int edgeid = 0;
			bw.write("<edges count=\"" + edges + "\">\n");
			for (int i = 0; i < nodes.size(); i++) {
				for (int j = 0; j < nodes.get(i).getLinks().size(); j++) {
					bw.write("<edge id=\""
							+ edgeid
							+ "\" source=\"S"
							+ nodes.get(i).getId()
							+ "\" target=\"S"
							+ nodes.get(i).getLinks().get(j).getTargetNode()
									.getId() + "\"/>\n");
					edgeid++;
				}
				// add tags to nodes
				Tree<TagNode> currentTree = trees.get(i);
				List<Node<TagNode>> allNodes = currentTree.toList();
				for (Node<TagNode> n : allNodes) {
					if (allNodes.indexOf(n) == 0) {
						// don't want to write root again
						continue;
					}
					bw.write("<edge id=\"" + edgeid + "\" source=\""
							+ n.getParent().data.getId() + "\" target=\""
							+ n.data.getId() + "\"/>\n");
					edgeid++;
				}

			}

			bw.write("</edges>\n");
			bw.write("</graph>\n");
			bw.write("</gexf>");
			bw.flush();
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void serverNodes2GephiWithspecificHormone(String filename,
			Content hormone) {
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
					filename)));
			// header
			bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
			bw.write("<gexf xmlns=\"http://www.gexf.net/1.1draft\" xmlns:viz=\"http://www.gexf.net/1.1draft/viz\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.gexf.net/1.1draft http://www.gexf.net/1.1draft/gexf.xsd\" version=\"1.1\">\n");
			bw.write("<graph defaultedgetype=\"undirected\" idtype=\"string\" type=\"static\">\n");
			// attributes
			bw.write("<attributes class=\"node\">\n");
			// for (int i = 0; i < Simulator.contentTypes; i++) {
			// bw.write("<attribute id=\"" + i + "\" title=\"c" + i +
			// "\" type=\"string\" />\n");
			// }
			bw.write("</attributes>\n");

			// nodes
			bw.write("<nodes count=\"" + nodes.size() + "\">\n");
			int edges = 0;
			for (int i = 0; i < nodes.size(); i++) {
				edges += nodes.get(i).getLinks().size();
				int nodeid = nodes.get(i).getId();
				SearchServerNode node = (SearchServerNode) nodes.get(i);
				String unitsforNode = "";
				ArrayList<UnitContainer> units = new ArrayList<UnitContainer>();
				if ((units = node.searchPresentUnits(hormone)).size() > 0) {
					for (UnitContainer u : units) {
						edges++;
						unitsforNode += "<node id=\"S" + nodeid + "_"
								+ u.getVideoUnit().getId() + "\" label=\""
								+ u.getVideoUnit().getId() + "\"></node>\n";
					}
				}

				// server node as vertex
				bw.write("<node id=\"S" + nodeid + "\" label=\"S"
						+ nodes.get(i).getId() + "\">\n");
				if (node.getHormoneTable().containsKey(hormone)) {
					bw.write("<viz:size value=\""
							+ node.getHormoneTable().get(hormone) + "\"/>\n");
				}
				bw.write("</node>\n");
				bw.write(unitsforNode);
			}
			bw.write("</nodes>\n");
			// edges
			int edgeid = 0;
			bw.write("<edges count=\"" + edges + "\">\n");
			for (int i = 0; i < nodes.size(); i++) {
				SearchServerNode node = (SearchServerNode) nodes.get(i);
				for (int j = 0; j < nodes.get(i).getLinks().size(); j++) {
					bw.write("<edge id=\""
							+ edgeid
							+ "\" source=\"S"
							+ nodes.get(i).getId()
							+ "\" target=\"S"
							+ nodes.get(i).getLinks().get(j).getTargetNode()
									.getId() + "\"/>\n");
					edgeid++;
				}
				// add units to nodes
				ArrayList<UnitContainer> units = new ArrayList<UnitContainer>();
				if ((units = node.searchPresentUnits(hormone)).size() > 0) {
					for (UnitContainer u : units) {
						bw.write("<edge id=\"" + edgeid + "\" source=\"S"
								+ node.getId() + "_" + u.getVideoUnit().getId()
								+ "\" target=\"S" + node.getId() + "\"/>\n");
						edgeid++;
					}
				}
			}

			bw.write("</edges>\n");
			bw.write("</graph>\n");
			bw.write("</gexf>");
			bw.flush();
			bw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}
